home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue55 / Splat / recordmain.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2000-01-26  |  11.7 KB  |  404 lines

  1. unit RecordMain;
  2.  
  3. // Splat.
  4. // Record a wave form for use in the Splat program.
  5. // Copyright ⌐ 2000 Tempest Software, Inc.
  6.  
  7. interface
  8.  
  9. uses
  10.   Windows, Messages, MMSystem, SysUtils, Classes, Graphics, Controls, Menus,
  11.   Forms, Dialogs, ComCtrls, StdCtrls, ActnList, ImgList;
  12.  
  13. type
  14.   EMciError = class(Exception)
  15.   private
  16.     fErrorCode: LongWord;
  17.   public
  18.     constructor Create(ErrorCode: LongWord); overload;
  19.     constructor Create(ErrorCode: LongWord; const Msg: string); overload;
  20.     constructor Create(ErrorCode: LongWord; const Fmt: string; const Args: array of const); overload;
  21.     property ErrorCode: LongWord read fErrorCode;
  22.   end;
  23.   TSortColumn = (scName, scSize, scDate);
  24.   TForm1 = class(TForm)
  25.     Label1: TLabel;
  26.     WaveList: TListView;
  27.     StatusBar: TStatusBar;
  28.     PopupMenu: TPopupMenu;
  29.     ActionList: TActionList;
  30.     PlayAction: TAction;
  31.     DeleteAction: TAction;
  32.     Play1: TMenuItem;
  33.     Delete1: TMenuItem;
  34.     ImageList: TImageList;
  35.     ChDirAction: TAction;
  36.     ChangeDirectory1: TMenuItem;
  37.     N1: TMenuItem;
  38.     ViewList1: TMenuItem;
  39.     ViewDetails1: TMenuItem;
  40.     procedure FormKeyUp(Sender: TObject; var Key: Word;
  41.       Shift: TShiftState);
  42.     procedure FormKeyDown(Sender: TObject; var Key: Word;
  43.       Shift: TShiftState);
  44.     procedure ActionListUpdate(Action: TBasicAction; var Handled: Boolean);
  45.     procedure DeleteActionExecute(Sender: TObject);
  46.     procedure PlayActionExecute(Sender: TObject);
  47.     procedure FormCreate(Sender: TObject);
  48.     procedure ChDirActionExecute(Sender: TObject);
  49.     procedure ViewStyleExecute(Sender: TObject);
  50.     procedure WaveListColumnClick(Sender: TObject; Column: TListColumn);
  51.     procedure WaveListCompare(Sender: TObject; Item1, Item2: TListItem;
  52.       Data: Integer; var Compare: Integer);
  53.     procedure WaveListDeletion(Sender: TObject; Item: TListItem);
  54.   private
  55.     { Private declarations }
  56.     RecordKey: Word;
  57.     SortColumn: TSortColumn;
  58.     SortAscending: Boolean;
  59.     procedure SetMode(const Mode: string);
  60.     procedure SetStatusInfo(const Info: string);
  61.     procedure GetWaveFiles;
  62.     function AddWaveFile(const FileName: string; Size: Integer = 0;
  63.       Date: TDateTime = 0): TListItem;
  64.   public
  65.     { Public declarations }
  66.   end;
  67.  
  68. var
  69.   Form1: TForm1;
  70.  
  71. implementation
  72.  
  73. uses CommCtrl, FileCtrl, KeyText;
  74.  
  75. {$R *.DFM}
  76.  
  77. resourcestring
  78.   sRecording = 'Recording';
  79.   sRecorded = 'Recorded %s';
  80.   sKilo = 'KB';
  81.  
  82. const
  83.   KiloBytes = 1024;
  84.  
  85. // Check a return code from an MCI function. Raise an exception for any error.
  86. procedure MciCheck(ErrorCode: LongWord); overload;
  87. begin
  88.   if ErrorCode <> 0 then
  89.     raise EMciError.Create(ErrorCode);
  90. end;
  91.  
  92. // Check a return code from an MCI function. Raise an exception for any error.
  93. // Use Msg as the exception message.
  94. procedure MciCheck(ErrorCode: LongWord; const Msg: string); overload;
  95. begin
  96.   if ErrorCode <> 0 then
  97.     raise EMciError.Create(ErrorCode, Msg);
  98. end;
  99.  
  100. // Check a return code from an MCI function. Raise an exception for any error.
  101. // Format an exception message from Fmt and Args.
  102. procedure MciCheck(ErrorCode: LongWord; const Fmt: string; const Args: array of const); overload;
  103. begin
  104.   if ErrorCode <> 0 then
  105.     raise EMciError.Create(ErrorCode, Fmt, Args);
  106. end;
  107.  
  108. { EMciError }
  109. // Exception class for MCI errors.
  110. constructor EMciError.Create(ErrorCode: LongWord);
  111. begin
  112.   Create(ErrorCode, '');
  113. end;
  114.  
  115. constructor EMciError.Create(ErrorCode: LongWord; const Msg: string);
  116. var
  117.   Buffer: array[0..128] of Char;
  118. begin
  119.   fErrorCode := ErrorCode;
  120.   MciGetErrorString(ErrorCode, Buffer, SizeOf(Buffer));
  121.   inherited Create(Msg + Buffer);
  122. end;
  123.  
  124. constructor EMciError.Create(ErrorCode: LongWord; const Fmt: string;
  125.   const Args: array of const);
  126. begin
  127.   Create(ErrorCode, Format(Fmt, Args));
  128. end;
  129.  
  130.  
  131. { TForm1 }
  132.  
  133. type
  134.   // Keep basic information about each file as the associated data
  135.   // in the list view.
  136.   PFileInfo = ^TFileInfo;
  137.   TFileInfo = record
  138.     Size: Integer;
  139.     Date: TDateTime;
  140.   end;
  141.  
  142. // Add a file to the list view.
  143. function TForm1.AddWaveFile(const FileName: string; Size: Integer; Date: TDateTime): TListItem;
  144. var
  145.   Search: TSearchRec;
  146.   Info: PFileInfo;
  147. begin
  148.   Result := WaveList.Items.Add;
  149.   Result.Caption := FileName;
  150.   if (Size = 0) or (Date = 0) then
  151.   begin
  152.     if FindFirst(FileName, 0, Search) = 0 then
  153.     begin
  154.       Size := Search.Size;
  155.       Date := FileDateToDateTime(Search.Time);
  156.       FindClose(Search);
  157.     end
  158.   end;
  159.   New(Info);
  160.   Info.Size := Size;
  161.   Info.Date := Date;
  162.   Result.Data := Info;
  163.   Result.SubItems.Add(IntToStr(Size div KiloBytes) + sKilo);
  164.   Result.SubItems.Add(DateTimeToStr(Date));
  165. end;
  166.  
  167. // Start recording a wave file for the key that the user has
  168. // pressed. Record only the first key pressed until the user
  169. // releases that key.
  170. procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  171.   Shift: TShiftState);
  172. resourcestring
  173.   sCannotOpen = 'Cannot open WAV recorder:'#13#10;
  174.   sCannotRecord = 'Cannot record WAV file:'#13#10;
  175. begin
  176.   if RecordKey = 0 then
  177.   begin
  178.     // Not already recording a sound, so start recording.
  179.     MciCheck(mciSendString('open new type waveaudio alias wave', nil, 0, 0), sCannotOpen);
  180.     MciCheck(mciSendString('record wave', nil, 0, 0), sCannotRecord);
  181.  
  182.     // Remember which key is being recorded, and update the status bar.
  183.     RecordKey := Key;
  184.     SetMode(sRecording);
  185.     SetStatusInfo(KeyCodeToDisplay(Key));
  186.   end;
  187. end;
  188.  
  189. // Stop recording when the user releases the key. Make sure
  190. // the user is releasing the key that is being recorded (in case
  191. // the user presses multiple keys).
  192. procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word;
  193.   Shift: TShiftState);
  194. resourcestring
  195.   sCannotStop = 'Cannot stop recording WAV file:'#13#10;
  196.   sCannotSave = 'Cannot save WAV file (%s):'#13#10;
  197.   sCannotClose = 'Cannot close WAV recorder:'#13#10;
  198. var
  199.   FileName: string;
  200.   Item: TListItem;
  201. begin
  202.   if Key = RecordKey then
  203.   begin
  204.     MciCheck(mciSendString('stop wave', nil, 0, 0), sCannotStop);
  205.  
  206.     // Save the waveform to a file.
  207.     FileName := KeyCodeToText(RecordKey) + '.wav';
  208.     MciCheck(mciSendString(PChar('save wave ' + FileName), nil, 0, 0),
  209.              Format(sCannotSave, [FileName]));
  210.  
  211.     MciCheck(mciSendString('close wave', nil, 0, 0), sCannotClose);
  212.  
  213.     RecordKey := 0;
  214.     SetMode('');
  215.     SetStatusInfo(Format(sRecorded, [FileName]));
  216.  
  217.     // If the file is not already in the list, add it.
  218.     Item := WaveList.FindCaption(0, FileName, False, True, True);
  219.     if Item = nil then
  220.       Item := AddWaveFile(FileName);
  221.     Item.Selected := True;
  222.   end;
  223. end;
  224.  
  225. // Set the status mode in the left-hand panel of the status bar.
  226. procedure TForm1.SetMode(const Mode: string);
  227. begin
  228.   StatusBar.Panels[0].Text := Mode;
  229. end;
  230.  
  231. // Set the status information in the right-hand panel of the status bar.
  232. procedure TForm1.SetStatusInfo(const Info: string);
  233. begin
  234.   StatusBar.Panels[1].Text := Info;
  235. end;
  236.  
  237. // Enable or disable actions according to the list view selection.
  238. // Only one file can be played at a time, so enable the Play action
  239. // only when exactly one file is selected.
  240. // Enable Delete when one or more files is selected.
  241. procedure TForm1.ActionListUpdate(Action: TBasicAction; var Handled: Boolean);
  242. begin
  243.   PlayAction.Enabled := WaveList.SelCount = 1;
  244.   DeleteAction.Enabled := WaveList.SelCount > 0;
  245. end;
  246.  
  247. // Delete the selected file or files. Confirm the deletion with the user first.
  248. // If one file is selected, show the file name in the prompt. Otherwise,
  249. // just show the number of files to be deleted.
  250. procedure TForm1.DeleteActionExecute(Sender: TObject);
  251. resourcestring
  252.   sConfirmOne = 'Are you sure you want to delete %s?';
  253.   sDeletedOne = '%s deleted';
  254.   sConfirmMany = 'Are you sure you want to delete the selected files?';
  255.   sDeletedMany = '%d files deleted';
  256. var
  257.   FileName: string;
  258.   Count, I: Integer;
  259. begin
  260.   Assert(WaveList.Selected <> nil);
  261.   if WaveList.SelCount = 1 then
  262.   begin
  263.     FileName := WaveList.Selected.Caption;
  264.     if mrYes = MessageDlg(Format(sConfirmOne, [FileName]), mtConfirmation, [mbYes, mbNo], 0) then
  265.     begin
  266.       WaveList.Selected.Delete;
  267.       DeleteFile(FileName);
  268.       SetStatusInfo(Format(sDeletedOne, [FileName]));
  269.     end;
  270.   end
  271.   else if mrYes = MessageDlg(sConfirmMany, mtConfirmation, [mbYes, mbNo], 0) then
  272.   begin
  273.     Count := 0;
  274.     for I := WaveList.Items.Count-1 downto 0 do
  275.     begin
  276.       if WaveList.Items[I].Selected then
  277.       begin
  278.         FileName := WaveList.Items[I].Caption;
  279.         WaveList.Items[I].Delete;
  280.         if DeleteFile(FileName) then
  281.           Inc(Count);
  282.       end;
  283.     end;
  284.     SetStatusInfo(Format(sDeletedMany, [Count]));
  285.   end;
  286. end;
  287.  
  288. // Play the selected file.
  289. procedure TForm1.PlayActionExecute(Sender: TObject);
  290. begin
  291.   if WaveList.Selected <> nil then
  292.     Win32Check(PlaySound(PChar(WaveList.Selected.Caption), 0, Snd_FileName or Snd_NoDefault or Snd_Async));
  293.   SetStatusInfo('');
  294. end;
  295.  
  296. // Start the program by fetching all the .WAV files in the current directory.
  297. procedure TForm1.FormCreate(Sender: TObject);
  298. begin
  299.   GetWaveFiles;
  300. end;
  301.  
  302. // Get all the .WAV files in the current directory and show them
  303. // in the list view.
  304. procedure TForm1.GetWaveFiles;
  305. resourcestring
  306.   sCaption = 'Record Sounds - ';
  307. var
  308.   Search: TSearchRec;
  309. begin
  310.   WaveList.Items.BeginUpdate;
  311.   try
  312.     // Add the directory name to the form and application captions.
  313.     Caption := sCaption + GetCurrentDir;
  314.     Application.Title := Caption;
  315.     WaveList.Items.Clear;
  316.     if FindFirst('*.wav', faAnyFile, Search) = 0 then
  317.       try
  318.         repeat
  319.           if (Search.Attr and faDirectory) = 0 then
  320.             AddWaveFile(Search.Name, Search.Size, FileDateToDateTime(Search.Time));
  321.         until FindNext(Search) <> 0;
  322.       finally
  323.         FindClose(Search);
  324.       end;
  325.   finally
  326.     WaveList.Items.EndUpdate;
  327.   end;
  328. end;
  329.  
  330. // Change directories and get the .WAV files in the new directory.
  331. procedure TForm1.ChDirActionExecute(Sender: TObject);
  332. resourcestring
  333.   sDlgCaption = 'Select folder for WAV files';
  334. var
  335.   Dir: string;
  336. begin
  337.   if SelectDirectory(sDlgCaption, '', Dir) then
  338.   begin
  339.     if not SysUtils.SetCurrentDir(Dir) then
  340.       RaiseLastWin32Error;
  341.     GetWaveFiles;
  342.   end;
  343. end;
  344.  
  345. // Change the list view style.
  346. procedure TForm1.ViewStyleExecute(Sender: TObject);
  347. begin
  348.   WaveList.ViewStyle := TViewStyle((Sender as TComponent).Tag);
  349.   (Sender as TMenuItem).Checked := True;
  350. end;
  351.  
  352. // Change the sort order of the list view.
  353. procedure TForm1.WaveListColumnClick(Sender: TObject; Column: TListColumn);
  354. begin
  355.   if SortColumn = TSortColumn(Column.Tag) then
  356.     SortAscending := not SortAscending
  357.   else
  358.   begin
  359.     SortColumn := TSortColumn(Column.Tag);
  360.     SortAscending := True;
  361.   end;
  362.   WaveList.AlphaSort;
  363. end;
  364.  
  365. procedure TForm1.WaveListCompare(Sender: TObject; Item1, Item2: TListItem;
  366.   Data: Integer; var Compare: Integer);
  367. resourcestring
  368.   sCannotHappen = 'WaveListCompare: internal error, SortColumn=%d';
  369. var
  370.   Info1, Info2: PFileInfo;
  371. begin
  372.   Info1 := Item1.Data;
  373.   Info2 := Item2.Data;
  374.   Assert(Info1 <> nil);
  375.   Assert(Info2 <> nil);
  376.   case SortColumn of
  377.   scName:
  378.     Compare := AnsiCompareFileName(Item1.Caption, Item2.Caption);
  379.   scSize:
  380.     Compare := Info1.Size - Info2.Size;
  381.   scDate:
  382.     if Info1.Date > Info2.Date then
  383.       Compare := 1
  384.     else if Info1.Date < Info2.Date then
  385.       Compare := -1
  386.     else
  387.       Compare := 0;
  388.   else
  389.     raise Exception.CreateFmt(sCannotHappen, [Ord(SortColumn)]);
  390.   end;
  391.  
  392.   if not SortAscending then
  393.     Compare := -Compare;
  394. end;
  395.  
  396. // When a list view item is removed from the list view,
  397. // delete the associated data record.
  398. procedure TForm1.WaveListDeletion(Sender: TObject; Item: TListItem);
  399. begin
  400.   FreeMem(Item.Data);
  401. end;
  402.  
  403. end.
  404.